package models;

import java.util.*;

import javax.persistence.*;

import net.sf.oval.constraint.MaxSize;

import org.apache.commons.mail.EmailException;

import play.data.binding.As;
import play.data.validation.Required;
import play.db.jpa.*;

/**
 * Idea Entity in order to manage creating ideas and adding them to whether
 * topic or entity or organization
 * 
 * @author Ghada Abu Shady
 */

@Entity
public class Idea extends play.db.jpa.Model {
	public boolean merge = false;
	/**
	 * The number of times that the system will check whether the idea is active
	 * or not
	 * 
	 * @author Nourhan El-Nehlawy
	 */
	public int notifications = 30;

	/**
	 * The title of an Idea, it can't be duplicate in the database
	 * 
	 * @author Ghada Abu Shady
	 */
	@Required
	public String title;

	/**
	 * The Body of the Idea
	 * 
	 * @author Ghada Abu Shady
	 */
	@Lob
	@Required
	@MaxSize(10000)
	public String body;

	/**
	 * an indicator to see whether the idea is marked as spam or not
	 * 
	 * @author Ghada Abu Shady
	 */
	public Boolean spam;

	/**
	 * the date the idea is created
	 * 
	 * @author Ghada Abu Shady
	 */
	@Required
	@As("yyyy-MM-dd")
	public Date postedAt;

	/**
	 * The author of Idea. Many ideas can be posted by one user
	 * 
	 * @author Ghada Abu Shady
	 */
	@ManyToOne
	public User author;

	/**
	 * The topic that the idea belongs to many ideas can belong to one topic
	 * 
	 * @author Ghada Abu Shady
	 */
	@ManyToOne
	public Topic topic;

	/**
	 * The entity that the idea may belong to directly many ideas can belong to
	 * one entity
	 * 
	 * @author Ghada Abu Shady
	 */
	@ManyToOne
	public Entities entity;

	/**
	 * The organization that the idea may belong to directly many ideas can
	 * belong to one organization
	 * 
	 * @author Ghada Abu Shady
	 */
	@ManyToOne
	public Organization organization;

	/**
	 * the comments list that belongs to a certain Idea
	 * 
	 * @author Ghada Abu Shady
	 */
	@OneToMany(mappedBy = "idea", cascade = CascadeType.ALL)
	public List<Comment> comments;

	/**
	 * Indicator to see idea is executed or not
	 * 
	 * @author Ghada Abu Shady
	 */
	public boolean execution;

	/**
	 * indicator to see whether the idea is hidden or not
	 * 
	 * @author Ghada Abu Shady
	 */
	public boolean hide;

	/**
	 * indicator to see whether the idea is active by having comments or not
	 * 
	 * @author Ghada Abu Shady
	 */
	public boolean hasComments;

	/**
	 * rating value of the idea
	 * 
	 * @author Ghada Abu Shady
	 */
	public double rate;

	/**
	 * Value of Idea priority accoording to each user preference
	 * 
	 * @author Ghada Abu Shady
	 */
	public String priority;

	/**
	 * the below attribute is for the number of raters of a certain idea
	 * 
	 * @author Ismail Omar
	 */

	public int numberOfRaters;
	/**
	 * the below attribute is for the total ratings that is done
	 * 
	 * 
	 */

	public double totalRating;

	/**
	 * Tags that the idea can be tagged in. It can be tagged in many tags and
	 * tags contain many ideas
	 * 
	 * @author Ghada Abu Shady
	 */
	@ManyToMany
	public List<Tag> tags;

	/**
	 * The List of Users that marked this idea as spam
	 * 
	 * @author Shahir El Faramawy
	 */
	@JoinTable(name = "IdeaSpammers")
	@ManyToMany
	public List<User> ideaSpamReporter;
	/**
	 * String of the id of the user who performed Hide on his idea
	 * 
	 * @author Ghada Abu Shady
	 */
	public String hidePerformer = "";

	/**
	 * Constructor to manage creating Idea that belongs to a certain topic
	 * 
	 * @param author
	 *            creator of idea
	 * 
	 * @param title
	 *            Title of new idea
	 * 
	 * @param body
	 *            body of new idea
	 * 
	 * @param t
	 *            topic that the idea will be added to
	 * 
	 * @author Ghada Abu Shady
	 * @author Mai Selwanes only for the log statement
	 */
	public Idea(User author, String title, String body, Topic topic) {
		this.body = body;
		this.postedAt = new Date();
		this.spam = false;

		this.comments = new ArrayList<Comment>();
		this.title = title;
		this.author = author;
		this.execution = false;
		this.hide = false;
		this.hasComments = false;
		this.rate = 0;
		this.priority = "Intermediate";
		this.tags = new ArrayList<Tag>();
		this.topic = topic;
		this.numberOfRaters = 0;
		this.totalRating = 0;
		
		this.ideaSpamReporter = new ArrayList<User>();
		Log log = new Log(this.organization, author,
				" has created a new idea of title: " + title + " in topic: "
						+ topic.title + " in entity named: "
						+ this.topic.entity.entityName);

		log.save();
	}

	/**
	 * Constructor to manage creating Idea that belongs directly to an entity
	 * 
	 * @param author
	 *            creator of idea
	 * 
	 * @param title
	 *            Title of new idea
	 * 
	 * @param body
	 *            body of new idea
	 * 
	 * @param e
	 *            entity that the idea will be added directly to
	 * 
	 * @author Ghada Abu Shady
	 * @author Mai Selwanes only for the log statement
	 */

	public Idea(User author, String title, String body, Entities entity) {

		this.body = body;
		this.postedAt = new Date();
		this.spam = false;
		this.comments = new ArrayList<Comment>();
		this.title = title;
		this.author = author;
		this.execution = false;
		this.hide = false;
		this.hasComments = false;
		this.rate = 0;
		this.priority = "Intermediate";
		this.tags = new ArrayList<Tag>();
		this.entity = entity;
		
		this.ideaSpamReporter = new ArrayList<User>();
		Log log = new Log(this.organization, author,
				" has created a new idea of title: " + title
						+ "in entity named: " + this.entity.entityName);
		log.save();
	}

	/**
	 * Constructor to manage creating Idea that belongs directly to an
	 * organization
	 * 
	 * @param author
	 *            creator of idea
	 * 
	 * @param title
	 *            Title of new idea
	 * 
	 * @param body
	 *            body of new idea
	 * 
	 * @param o
	 *            organization that the idea will be added directly to
	 * 
	 * @author Ghada Abu Shady
	 * @author Mai Selwanes only for the log statement
	 */
	public Idea(User author, String title, String body,
			Organization organization) {

		this.body = body;
		this.postedAt = new Date();
		this.spam = false;
		this.comments = new ArrayList<Comment>();
		this.title = title;
		this.author = author;
		this.execution = false;
		this.hide = false;
		this.hasComments = false;
		this.rate = 0;
		this.priority = "Intermediate";
		this.tags = new ArrayList<Tag>();
		this.organization = organization;
		this.ideaSpamReporter = new ArrayList<User>();
		Log log = new Log(this.organization, author,
				" has created a new idea of title: " + title
						+ " in the organization.");
		log.save();

	}

	/**
	 * constructor to handle posting comments on ideas.
	 * 
	 * @param title
	 *            title of the idea
	 * @param body
	 *            body of the idea
	 * @author Nada Hassan
	 * @author Mai Selwanes just for the log statement
	 */
	public Idea(String title, String body) {
		this.title = title;
		this.body = body;
		this.rate=0;
		this.priority = "Intermediate";
	}
	public Idea(String title, String body,double rate) {
		this.title = title;
		this.body = body;
		this.rate=rate;
	}

	public void PrioratizeIdea(String priority) {
		this.priority = priority;
		User userNull = null;
		Log log = new Log(this.organization, userNull, this.title
				+ "has been prioritized as " + priority);
		log.save();
	}

	/*
	 * public Idea addComment(String author, String content) { Comment
	 * newComment = new Comment(this, author, content).save();
	 * this.comments.add(newComment); this.save(); return this; }
	 */

	/**
	 * the below method is the rating method for any idea in an organization
	 * 
	 * @param double x is an integer representing the rate input which is from 1 to
	 *        5
	 * 
	 * @author Ismail Omar
	 * @author Mai Selwanes only for the log statement
	 */

	public double rate(double rateInput) {
		double average = this.rate;
		this.numberOfRaters++;
		this.totalRating = this.totalRating + rateInput;
		average = totalRating / numberOfRaters;
		this.rate = average;
		User userNull = null;
		Log log = new Log(this.organization, userNull,
				" has rated the idea title: " + this.title + " as " + rateInput);
		log.save();
		return this.rate;
	}

	/**
	 * marks an idea as spams by a user. The organizer is then notified via
	 * e-mail and notification. This is also logged
	 * 
	 * @param user
	 *            user is the idea developer that marked this idea as spam.
	 * @throws exception
	 * 			throws an email exception           
	 * @author Shahir El Faramawy
	 * @author Mai Selwanes
	 */
	public void spamIdea(User user) throws EmailException {
		this.ideaSpamReporter.add(user);
		user.ideaSpammed.add(this);
		this.save();
		user.save();
		String content = "has spammed idea" + this.title + "created by"
				+ this.author;
		ArrayList<User> subscribers = new ArrayList<User>();
		if (this.organization != null) {
			subscribers.add(this.organization.lead);
			Email mail = new Email(user.email, this.organization.lead.email,
					"Spamming", user.name + "has spamed the idea" + this.title);
			mail.sendEmail();
		}
		if (this.entity != null) {
			for (int counter = 0; counter < entity.entityOrganizers.size(); counter++) {
				subscribers.add(this.entity.entityOrganizers.get(counter));
				Email mail = new Email(user.email, this.entity.entityOrganizers
						.get(counter).email, "Spamming", user.name
						+ "has spamed the idea" + this.title);
				mail.sendEmail();
			}
			subscribers.add(this.entity.organization.lead);
			Email mail = new Email(user.email,
					this.entity.organization.lead.email, "Spamming", user.name
							+ "has spamed the idea" + this.title);
			mail.sendEmail();
		}
		if (this.topic != null) {
			for (int counter = 0; counter < topic.entity.entityOrganizers
					.size(); counter++) {
				subscribers
						.add(this.topic.entity.entityOrganizers.get(counter));
				Email mail = new Email(user.email,
						this.topic.entity.entityOrganizers.get(counter).email,
						"Spamming", user.name + "has spamed the idea"
								+ this.title);
				mail.sendEmail();
			}
			subscribers.add(this.topic.entity.organization.lead);
			Email mail = new Email(user.email,
					this.topic.entity.organization.lead.email, "Spamming",
					user.name + "has spamed the idea" + this.title);
			mail.sendEmail();
		}

		user.sendNotificationWithType(content, subscribers, "Idea", this.id);

		Log log = new Log(this.organization, user,
				" has spammed the idea of title: " + this.title
						+ "created by user named " + this.author + ".");
		log.save();
	}

	/**
	 * ConfirmAsDuplicate method sends a mail by the organization lead to the
	 * organizer that the idea will be marked as duplicate. akes two ideas and
	 * check if there body or content.
	 * 
	 * @param user
	 *            that will responsible to mark idea as duplicate.
	 * @throws EmailException
	 * @Author Nada Hassan
	 * @author Mai Selwanes only for the log statement
	 */
	public void ConfirmAsDuplicate(User user) throws EmailException {
		Email duplicateConfirm = new Email(this.organization.lead.email,
				user.email, "Request Approved",
				"The request that idea is duplicate is approved." + this.title
						+ this.body);
		duplicateConfirm.sendEmail();
		Log log = new Log(this.organization, user,
				" has had his/her idea marked as duplicate.");
		log.save();

	}

	/**
	 * Method search in Ideas returns the list of Ideas with names that match
	 * the given string parameter search to search the keyword that will be used
	 * to be fetched from the database
	 * 
	 * @author Ahmed Ammar
	 */
	public static List<Idea> search(String search) {
		String likeSearch = search.toLowerCase();
		return Idea.find("LOWER(title) LIKE ?", likeSearch).fetch();
	}

	// /**
	// * This method takes the input from the user as a string
	// * and returns the list of ideas with that string and the number of
	// * ideas with that search found
	// * @Author Mohamed Ali
	// */
	// public static SearchIdeaResults searchForIdeas(String search)
	// {
	// //String likeSearch = "%"+search+"%";
	// Long count = count("title like ?",search);
	// List<Idea> ideas1=find("title like ?",search).fetch();
	// return new SearchIdeaResults(ideas1,count);

	// }

	/**
	 * This method takes the input from the user as a string and returns the
	 * list of ideas with that string and the number of ideas with that search
	 * found
	 * @return list of ideas and number of ideas
	 * @params search
	 * @Author Mohamed Ali
	 */
	public static SearchIdeaResults searchForIdeas(String search) {
		// String likeSearch = "%"+search+"%";
		String likeSearch="%"+search+"%";
		Long count = count("title like ?", likeSearch.toLowerCase());
		List<Idea> ideasOne = find("title like ?", likeSearch.toLowerCase()).fetch();
		return new SearchIdeaResults(ideasOne, count);
	}
	/**This method takes the input from the user of the rate and returns a list of 
	 * ideas with that rate or less 
	 * @param searchOne
	 * @return
	 */
	public static SearchIdeaResults searchForIdeasByRate(double searchOne) {
		Long count = count("rate < ?", searchOne);
		List<Idea> ideasOne = find("rate < ?", searchOne).fetch();
		return new SearchIdeaResults(ideasOne, count);
	} 
	// /**
	// * Method search in Ideas returns the list of Ideas with names that match
	// * the given string parameter search to search the keyword that will be
	// used
	// * to be fetched from the database
	// *
	// * @author Ahmed Ammar
	// */
	// public static List<Idea> search(String search) {
	// String likeSearch = search;
	// return Idea.find("title LIKE ?", likeSearch).fetch();
	// }

	/**
	 * This method gets the priority chosen by the user and searches for ideas
	 * with that priority and then returns a list of ideas with that priority
	 * and the number of ideas found with that priority by this filtering
	 * happens
	 * 
	 * @param PriorityText
	 * @return list of ideas and the number of ideas
	 * @Author Mohamed Ali
	 */
	public static FilterByPriority filterByPriority(String PriorityText) 
	{
		Long count = count("priority like ?", PriorityText.toLowerCase());
		List<Idea> ideasOne = find("priority like ?", PriorityText.toLowerCase()).fetch();
//		List<Idea> finalList = new ArrayList<Idea>();
//		if(!ideas1.isEmpty()){
//		for(int i = 0;i < ideas1.size();i++){
//			if(!ideas.isEmpty()){
//			for(int j = 0 ;j < ideas.size();j++){
//				if(ideas1.get(i) == ideas.get(j)){
//					finalList.add(ideas1.get(i));
//				}
//			}
//		}
//		}
//	}
	//	count = (long) finalList.size();
		return new FilterByPriority(ideasOne, count);
	}

	/**
	 * sending a notification with the idea being shared to a certain community
	 * to its subscribers.
	 * 
	 * @param user
	 *            the user that is sharing the idea and send the notification.
	 * @param community
	 *            the community in which the idea is shared into
	 * @author ismail omar
	 */
	public void shareIdeaInCommunity(User user, Community community) {
		String notificationMessage = " has shared the idea named "
				+ this.title + " in " + community.name + " Community";
		user.sendNotificationWithType(notificationMessage,
				community.subscribersToCommunity, "Community", community.id);
	}

	/**
	 * This method takes the user id and returns true if the type of the
	 * organization that he's searching in is public , OR False if the
	 * organization is private/Closed and he's Not a member in it and True
	 * otherwise
	 * 
	 * @param userId
	 *            the user's id
	 * @return returns the True or False
	 * @author Nourhan El-Nehlawy
	 */
	public boolean checkType(Long userId) {
		if (!this.organization.type.matches("Public")) {
			User user = User.findById(userId);
			if (this.organization.checkUser(user)) {
				return true;
			} else
				return false;
		} else
			return true;
	}

	/**
	 * RejectIdeaAsDuplicate method a reply sent by the organization lead to the
	 * user rejecting his request to mark or consider idea as duplicate.
	 * 
	 * @param user
	 *            organizer that sends the reject mail to the idea developer.
	 * @throws EmailException
	 * @author Nada Hassan
	 */
	public void RejectAsDuplicate(User user) throws EmailException {
		Email rejectMail = new Email(this.organization.lead.email, user.email,
				"Request Rejected",
				"The request of marking idea is duplicate is rejected");
		rejectMail.sendEmail();

	}

	/**
	 * this method gets the name of the idea
	 * 
	 * @return title
	 * @author Mohamed Ali
	 */
	public String getTitle() {
		return this.title;
	}

	
	// public void sendEmailDuplicate(long ideaId,String duplicate,User user)
	// throws EmailException
	// {
	// Idea idea = Idea.findById(ideaId);
	// System.out.println(ideaId+"   "+duplicate);
	// String IdeaTitle=idea.getTitle();
	// String s = " ' "+" "+ IdeaTitle +" ' and ' "+" "+duplicate+" ' ";
	// String source = this.organization.lead.email;
	// String destination = user.email;
	// Email e = new Email(source,destination ,
	// "Request for duplication ",
	// s);

	// e.sendEmail();
	// }

}
